🌳 Complete DOM Manipulation Guide
A comprehensive reference for working with the Document Object Model in JavaScript.
Table of Contents
- Selecting Elements
- Creating & Inserting Elements
- Removing & Replacing Elements
- Modifying Element Content
- Working with Attributes
- Manipulating Classes
- Inline Styles
- Traversing the DOM
- Event Handling
- Event Object Properties
- Common Event Types
- Form Events & Handling
- Document & Window Events
- Mouse & Pointer Events
- Keyboard Events
- Touch Events
- Drag & Drop API
- Intersection Observer API
- Mutation Observer API
- Resize Observer API
- Web Storage API
- Clipboard API
- Geolocation API
- Fetch API
- DOM Manipulation Performance Tips
1️⃣ Selecting Elements
getElementById()
Selects a single element by its ID attribute.
const element = document.getElementById('myId');
console.log(element);
// Returns: <div id="myId">...</div> or null
Notes:
- Fastest selection method
- Returns single element or null
- ID must be unique in document
getElementsByClassName()
Selects all elements with a specific class name.
const elements = document.getElementsByClassName('myClass');
console.log(elements); // HTMLCollection
// Convert to array
const array = Array.from(elements);
// Iterate
for (let el of elements) {
console.log(el);
}
Notes:
- Returns live HTMLCollection
- Updates automatically when DOM changes
- Case-sensitive
getElementsByTagName()
Selects all elements with a specific tag name.
const divs = document.getElementsByTagName('div');
const allElements = document.getElementsByTagName('*'); // All elements
// Select within specific element
const container = document.getElementById('container');
const spans = container.getElementsByTagName('span');
Notes:
- Returns live HTMLCollection
- Use '*' for all elements
- Can be called on any element, not just document
querySelector()
Selects the first element matching a CSS selector.
// By ID
const el1 = document.querySelector('#myId');
// By class
const el2 = document.querySelector('.myClass');
// By tag
const el3 = document.querySelector('div');
// Complex selectors
const el4 = document.querySelector('div.container > p:first-child');
const el5 = document.querySelector('[data-id="123"]');
const el6 = document.querySelector('input[type="email"]');
Notes:
- Most flexible selection method
- Returns first match or null
- Supports all CSS selectors
querySelectorAll()
Selects all elements matching a CSS selector.
const elements = document.querySelectorAll('.myClass');
// Returns static NodeList
console.log(elements.length);
// Iterate
elements.forEach(el => {
console.log(el);
});
// Convert to array
const array = [...elements];
// Complex selectors
const links = document.querySelectorAll('nav a[href^="http"]');
const checked = document.querySelectorAll('input:checked');
Notes:
- Returns static NodeList (not live)
- Supports all CSS selectors
- Use forEach() to iterate
Comparison: querySelector vs getElementById
// Performance test
console.time('getElementById');
for (let i = 0; i < 10000; i++) {
document.getElementById('test');
}
console.timeEnd('getElementById'); // ~3ms
console.time('querySelector');
for (let i = 0; i < 10000; i++) {
document.querySelector('#test');
}
console.timeEnd('querySelector'); // ~15ms
Recommendation: Use getElementById() when possible for best performance.
2️⃣ Creating & Inserting Elements
createElement()
Creates a new element node.
const div = document.createElement('div');
div.textContent = 'Hello World';
div.className = 'container';
div.id = 'main';
console.log(div); // <div class="container" id="main">Hello World</div>
createTextNode()
Creates a text node.
const text = document.createTextNode('Hello World');
const div = document.createElement('div');
div.appendChild(text);
createDocumentFragment()
Creates a lightweight document fragment for batch operations.
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
const li = document.createElement('li');
li.textContent = `Item ${i}`;
fragment.appendChild(li);
}
// Single DOM update
document.getElementById('list').appendChild(fragment);
Benefits:
- Improves performance
- Only one reflow/repaint
- Not part of the main DOM tree
appendChild()
Adds a node as the last child.
const parent = document.getElementById('parent');
const child = document.createElement('div');
child.textContent = 'New child';
parent.appendChild(child);
append()
Adds multiple nodes and strings at the end.
const parent = document.getElementById('parent');
// Can append multiple items
parent.append('Text', document.createElement('div'), 'More text');
// Can append text directly
parent.append('Hello World');
Differences from appendChild():
- Can append multiple nodes
- Can append text directly (no createTextNode needed)
- No return value
prepend()
Adds nodes at the beginning.
const parent = document.getElementById('parent');
const newFirst = document.createElement('div');
parent.prepend(newFirst);
parent.prepend('Text at start', newFirst);
insertBefore()
Inserts a node before a reference node.
const parent = document.getElementById('parent');
const newNode = document.createElement('div');
const referenceNode = document.querySelector('.reference');
parent.insertBefore(newNode, referenceNode);
insertAdjacentElement()
Inserts element at specified position.
const target = document.getElementById('target');
const newEl = document.createElement('div');
// 4 positions:
target.insertAdjacentElement('beforebegin', newEl); // Before target
target.insertAdjacentElement('afterbegin', newEl); // First child
target.insertAdjacentElement('beforeend', newEl); // Last child
target.insertAdjacentElement('afterend', newEl); // After target
Visual representation:
<!-- beforebegin -->
<div id="target">
<!-- afterbegin -->
content
<!-- beforeend -->
</div>
<!-- afterend -->
insertAdjacentHTML()
Inserts HTML string at specified position.
const target = document.getElementById('target');
target.insertAdjacentHTML('beforeend', '<p>New paragraph</p>');
target.insertAdjacentHTML('afterbegin', '<h1>Title</h1>');
Warning: Be careful with user input to avoid XSS attacks.
insertAdjacentText()
Inserts text at specified position (safe from XSS).
const target = document.getElementById('target');
const userInput = '<script>alert("XSS")</script>';
// Safe - treats as text, not HTML
target.insertAdjacentText('beforeend', userInput);
3️⃣ Removing & Replacing Elements
remove()
Removes the element from the DOM.
const element = document.getElementById('myElement');
element.remove();
removeChild()
Removes a child element.
const parent = document.getElementById('parent');
const child = document.getElementById('child');
parent.removeChild(child);
// Remove first child
if (parent.firstChild) {
parent.removeChild(parent.firstChild);
}
// Remove all children
while (parent.firstChild) {
parent.removeChild(parent.firstChild);
}
replaceChild()
Replaces a child element with another.
const parent = document.getElementById('parent');
const oldChild = document.getElementById('old');
const newChild = document.createElement('div');
newChild.textContent = 'New content';
parent.replaceChild(newChild, oldChild);
replaceWith()
Replaces element with new nodes.
const oldElement = document.getElementById('old');
const newElement = document.createElement('div');
// Replace with single element
oldElement.replaceWith(newElement);
// Replace with multiple nodes
oldElement.replaceWith('Text ', newElement, ' more text');
cloneNode()
Creates a copy of a node.
const original = document.getElementById('original');
// Shallow clone (no children)
const shallowClone = original.cloneNode(false);
// Deep clone (includes children)
const deepClone = original.cloneNode(true);
document.body.appendChild(deepClone);
Notes:
- Event listeners are NOT copied
- IDs should be changed to avoid duplicates
4️⃣ Modifying Element Content
innerHTML
Gets or sets HTML content.
const div = document.getElementById('myDiv');
// Get
console.log(div.innerHTML);
// Set
div.innerHTML = '<p>New <strong>HTML</strong> content</p>';
// Append
div.innerHTML += '<p>More content</p>';
Warning:
- Vulnerable to XSS if using user input
- Replaces all content (destroys event listeners)
textContent
Gets or sets text content (no HTML parsing).
const div = document.getElementById('myDiv');
// Get all text (ignores HTML tags)
console.log(div.textContent);
// Set (HTML tags rendered as text)
div.textContent = '<p>This is not HTML</p>';
// More performant than innerHTML for text-only
Safe from XSS attacks.
innerText
Gets or sets visible text content.
const div = document.getElementById('myDiv');
// Get visible text only (respects CSS display)
console.log(div.innerText);
// Set
div.innerText = 'New text';
Differences from textContent:
- Respects CSS (hidden elements ignored)
- Triggers reflow (slower)
- Not available on all node types
outerHTML
Gets or sets element and its content.
const div = document.getElementById('myDiv');
// Get (includes the element itself)
console.log(div.outerHTML);
// <div id="myDiv">content</div>
// Set (replaces entire element)
div.outerHTML = '<section>New element</section>';
Comparison: Setting Content
const container = document.getElementById('container');
// Method 1: innerHTML (parses HTML, vulnerable to XSS)
container.innerHTML = '<p>Hello</p>';
// Method 2: textContent (safe, text only)
container.textContent = 'Hello';
// Method 3: createElement + appendChild (safest, most control)
const p = document.createElement('p');
p.textContent = 'Hello';
container.appendChild(p);
5️⃣ Working with Attributes
getAttribute()
Gets an attribute value.
const link = document.querySelector('a');
const href = link.getAttribute('href');
const target = link.getAttribute('target');
const dataId = link.getAttribute('data-id');
console.log(href); // "https://example.com"
setAttribute()
Sets an attribute value.
const link = document.querySelector('a');
link.setAttribute('href', 'https://newurl.com');
link.setAttribute('target', '_blank');
link.setAttribute('data-id', '123');
link.setAttribute('rel', 'noopener noreferrer');
removeAttribute()
Removes an attribute.
const link = document.querySelector('a');
link.removeAttribute('target');
link.removeAttribute('data-id');
hasAttribute()
Checks if attribute exists.
const link = document.querySelector('a');
if (link.hasAttribute('target')) {
console.log('Has target attribute');
}
if (link.hasAttribute('data-custom')) {
console.log('Has custom data attribute');
}
attributes
Gets all attributes as NamedNodeMap.
const element = document.querySelector('div');
// Get all attributes
const attrs = element.attributes;
for (let attr of attrs) {
console.log(`${attr.name}: ${attr.value}`);
}
// Check specific attribute
console.log(attrs['id'].value);
console.log(attrs.getNamedItem('class').value);
Property vs Attribute
const input = document.querySelector('input');
// Attribute (HTML attribute)
input.getAttribute('value'); // Initial value
input.setAttribute('value', 'new');
// Property (DOM property, reflects current state)
input.value; // Current value
input.value = 'new';
// Example
input.setAttribute('value', 'hello');
console.log(input.getAttribute('value')); // "hello"
console.log(input.value); // "hello"
// User types "world"
console.log(input.getAttribute('value')); // Still "hello"
console.log(input.value); // "world"
Key differences:
- Attributes: HTML attributes (strings only)
- Properties: JavaScript object properties (any type)
- Properties are usually preferred
data-* Attributes (dataset)
// HTML: <div id="user" data-id="123" data-name="John" data-user-role="admin">
const div = document.getElementById('user');
// Read
console.log(div.dataset.id); // "123"
console.log(div.dataset.name); // "John"
console.log(div.dataset.userRole); // "admin" (camelCase)
// Write
div.dataset.id = '456';
div.dataset.newAttr = 'value';
// Delete
delete div.dataset.name;
// Check existence
if ('id' in div.dataset) {
console.log('Has data-id');
}
Notes:
- Hyphenated names become camelCase
- All values are strings
- Changes reflect in HTML attributes
6️⃣ Manipulating Classes
className
Gets or sets class attribute as string.
const div = document.getElementById('myDiv');
// Get
console.log(div.className); // "class1 class2 class3"
// Set (replaces all classes)
div.className = 'newClass';
// Append
div.className += ' anotherClass';
classList
Provides methods to manipulate classes.
const div = document.getElementById('myDiv');
// Add single or multiple classes
div.classList.add('newClass');
div.classList.add('class1', 'class2', 'class3');
// Remove
div.classList.remove('oldClass');
div.classList.remove('class1', 'class2');
// Toggle (add if absent, remove if present)
div.classList.toggle('active');
div.classList.toggle('hidden', false); // Force remove
div.classList.toggle('visible', true); // Force add
// Check if contains
if (div.classList.contains('active')) {
console.log('Element is active');
}
// Replace
div.classList.replace('oldClass', 'newClass');
// Iterate
div.classList.forEach(className => {
console.log(className);
});
// Get as array
const classes = [...div.classList];
classList vs className
const div = document.getElementById('myDiv');
div.className = 'btn btn-primary active';
// classList - Recommended
div.classList.add('disabled'); // "btn btn-primary active disabled"
div.classList.remove('active'); // "btn btn-primary disabled"
// className - Requires string manipulation
div.className += ' disabled';
div.className = div.className.replace('active', '');
Recommendation: Use classList for better readability and safety.